package it.infodea.tapestrydea.services.jcr.impl;

import it.infodea.tapestrydea.services.jcr.JcrAssetStreamer;
import it.infodea.tapestrydea.services.jcr.JcrSessionProviderService;
import it.infodea.tapestrydea.services.jcr.nodes.wrappers.JcrFileNode;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Map;

import javax.jcr.Node;
import javax.jcr.PathNotFoundException;
import javax.jcr.RepositoryException;
import javax.jcr.Session;

import org.apache.tapestry5.internal.TapestryInternalUtils;
import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
import org.apache.tapestry5.services.InvalidationListener;
import org.apache.tapestry5.services.Response;

public class JcrAssetStreamerImpl implements JcrAssetStreamer, InvalidationListener {

	private final JcrSessionProviderService jcrSessionProvider;

	private final Response response;

	private boolean useCache = false;

	private final Map<String, ByteArrayOutputStream> cache = CollectionFactory.newConcurrentMap();

	public JcrAssetStreamerImpl(JcrSessionProviderService jcrSessionProvider, Response response) {
		this.jcrSessionProvider = jcrSessionProvider;
		this.response = response;
	}

	public void streamJcrAsset(String clientData) throws IOException, PathNotFoundException, RepositoryException {
		Session session = jcrSessionProvider.getSession();
		if (session.itemExists(clientData)) {
			Node node = (Node) session.getItem(clientData);
			JcrFileNode jcrNode = new JcrFileNode(node);
			String contentType = jcrNode.getMimeType();
			ByteArrayOutputStream stream = getDataStream(jcrNode);
			response.setContentLength(stream.size());
			OutputStream output = response.getOutputStream(contentType);
			stream.writeTo(output);
			output.close();
		}
	}

	private ByteArrayOutputStream getDataStream(JcrFileNode jcrNode) throws IOException, RepositoryException {
		String key = getKey(jcrNode);
		ByteArrayOutputStream result = getCache(key);
		if (result == null) {
			result = constructJcrAssetStream(jcrNode);
			putCache(key, result);
		}
		return result;
	}

	private void putCache(String key, ByteArrayOutputStream result) {
		if (useCache) {
			cache.put(key, result);
		}
	}

	private ByteArrayOutputStream getCache(String key) {
		return useCache ? cache.get(key) : null;
	}

	private ByteArrayOutputStream constructJcrAssetStream(JcrFileNode jcrNode) throws IOException, PathNotFoundException,
			RepositoryException {
		ByteArrayOutputStream result = new ByteArrayOutputStream();
		InputStream inputStream = null;
		try {
			inputStream = jcrNode.getDataStream();
			TapestryInternalUtils.copy(inputStream, result);
		} finally {
			if (inputStream != null) {
				inputStream.close();
			}
		}
		return result;
	}

	private String getKey(JcrFileNode jcrNode) throws RepositoryException {
		return jcrNode.getUUID() + jcrNode.getLastModified().getTimeInMillis();
	}

	public void objectWasInvalidated() {
		cache.clear();
	}

}
